package com.xiaoyu.base.serverpush.data;

import androidx.annotation.Nullable;

import android.text.TextUtils;

import com.xiaoyu.net.request.RequestWithJsonDataReturn;
import com.xiaoyu.base.config.HttpConfig;
import com.xiaoyu.base.serverpush.EventCompleteListener;
import com.xiaoyu.base.serverpush.ServerEventFactory;
import com.xiaoyu.base.serverpush.event.ServerPushBaseEvent;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import in.srain.cube.request.FailData;
import in.srain.cube.request.JsonData;
import in.srain.cube.request.RequestData;
import in.srain.cube.request.RequestHandler;
import in.srain.cube.util.CLog;

public class ServerPushEventData implements EventCompleteListener {

    private static final String TAG = ServerPushEventData.class.getSimpleName();

    private static ServerPushEventData sInstance = new ServerPushEventData();

    private final Map<String, ServerPushBaseEvent> mEvents = new ConcurrentHashMap<>();
    private final Map<String, Long> mEventTimestamps = new ConcurrentHashMap<>();

    public static ServerPushEventData getInstance() {
        return sInstance;
    }

    public synchronized void processEventList(JsonData rawEventList) {
        if (rawEventList.length() <= 0) {
            return;
        }
        CLog.d(TAG, "processEventList: %s", rawEventList);
        final List<JsonData> rawJsonList = rawEventList.toList();
        for (JsonData itemJson : rawJsonList) {
            CLog.d(TAG, "item json: %s", itemJson);
            processEvent(itemJson);
        }
    }

    public synchronized void processEvent(JsonData itemJson) {
        final ServerPushBaseEvent event = ServerEventFactory.createServerPushEvent(itemJson);
        if (event == null || !checkEvent(event, itemJson)) {
            return;
        }

        CLog.d(TAG, "processEvent: %s", event);
        mEvents.put(event.eventId, event);
        event.setEventCompleteListener(this);
        event.execute();
    }

    private boolean checkEvent(@Nullable ServerPushBaseEvent event, JsonData itemJson) {
        if (event == null) {
            CLog.w(TAG, "create event null: %s", itemJson);
            return false;
        }
        if (mEvents.containsKey(event.eventId)) {
            CLog.d(TAG, "event id already exist: %s", event.eventId);
            return false;
        }
        if (!TextUtils.isEmpty(event.timestampKey) && event.timestamp > 0) {
            Long preTimestamp = mEventTimestamps.get(event.timestampKey);
            if (preTimestamp != null && preTimestamp > event.timestamp) {
                CLog.d(TAG, "event timestamp expire: %s %s", event.timestampKey, event.timestamp);
                return false;
            }
            mEventTimestamps.put(event.timestampKey, event.timestamp);
        }
        return true;
    }

    private void postEventReceipt(ServerPushBaseEvent event) {
        CLog.i(TAG, "postEventReceipt: %s", event);
        final RequestWithJsonDataReturn<JsonData> request = new RequestWithJsonDataReturn<>();
        RequestData requestData = request.getRequestData().setRequestUrl(HttpConfig.EVENT_READ);
        requestData.addQueryData("id", event.eventId);
        request.setRequestHandler(new RequestHandler<JsonData, JsonData>() {
            @Override
            public JsonData processOriginData(JsonData originData) {
                return originData;
            }

            @Override
            public void onRequestFinish(JsonData jsonData) {
                removeEvent(event);
            }

            @Override
            public void onRequestFail(FailData failData) {
                CLog.e(TAG, "postEventReceipt fail: %s", event);
            }
        });
        request.enqueue();
    }

    private void removeEvent(ServerPushBaseEvent event) {
        CLog.i(TAG, "removeEvent: %s", event);
        mEvents.remove(event.eventId);
    }

    @Override
    public void onComplete(ServerPushBaseEvent event) {
        CLog.i(TAG, "onComplete: %s", event);
        if (event.needReceipt) {
            postEventReceipt(event);
        } else {
            removeEvent(event);
        }
    }
}
